// -*- C++ -*-
//
//  This file is part of Siena, a wide-area event notification system.
//  See http://www.inf.usi.ch/carzaniga/siena/
//
//  Authors: Antonio Carzaniga
//  See the file AUTHORS for full details. 
//
//  Copyright (C) 2003-2004 University of Colorado
//  Copyright (C) 2005 Antonio Carzaniga
//
//  This program is free software; you can redistribute it and/or
//  modify it under the terms of the GNU General Public License
//  as published by the Free Software Foundation; either version 2
//  of the License, or (at your option) any later version.
//
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU General Public License for more details.
//
//  You should have received a copy of the GNU General Public License
//  along with this program; if not, write to the Free Software
//  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
//  USA, or send email to one of the authors.
//
//
// $Id: bset_encoding.h,v 1.8 2010-03-11 15:16:39 carzanig Exp $
//
#ifndef BSET_ENCODING_H
#define BSET_ENCODING_H

#include "ConstsSff.h"
#include "siena/types.h"
#include "hash.h"

/** \file bset_encoding.h
 *
 *  This file defines the functions that implement the encoding of
 *  attributes and constraints into strings and then into Bloom
 *  filters.  All the functions are parameterized (i.e., they are
 *  templates) with respect to the Bloom filter type (bset_type).
 *  These functions require that bset_type implement the following:
 *
 *  set_bit(unsigned int i)  :: sets the i-th bit
 *  unsigned int b.size()    :: size of the Bloom filters in bits
 **/
namespace siena {

#ifndef CONFIG_BLOOM_FILTER_K
#error "You must define CONFIG_BLOOM_FILTER_K"
#endif 

//
// this defines a set of K hash functions to be used to set bits in
// Bloom filters.  This class simply uses PHash objects, which
// implement a hash function using a polynomial-mod-P, each
// parameterized with a different base of the polynomial.  So, each
// new character or input value is added to all hash functions, and
// then all hash functions are used to set bits in the given Bloom
// filter.
//
template<class bset_type>
class k_hash_setter {
private:
    static const unsigned int k = CONFIG_BLOOM_FILTER_K;
    PHash h[CONFIG_BLOOM_FILTER_K];

public:
    k_hash_setter() {
	for(unsigned int i = 0; i < k; ++i)
	    h[i].set_base(i + 32);
    };

    void add(int c) {
	for(unsigned int i = 0; i < k; ++i)
	    h[i].add(c);
    }

    void add_double(double f) {
	for(unsigned int i = 0; i < k; ++i)
	    h[i].add(f);
    }

    void add(const char * begin, const char * end) {
	for(unsigned int i = 0; i < k; ++i)
	    h[i].add(begin, end);
    }

    void set_bits(bset_type & b) {
	for(unsigned int i = 0; i < k; ++i)
	    b.set_bit(h[i].value() % b.size());
    }
};

//
// encode_equals encodes a constraint or an attribute as an *equality*
// category.
//
template<class bset_type>
void encode_equals(bset_type & b, const attribute * a) throw() {
    k_hash_setter<bset_type> hash_setter;

    siena::string_t name = a->name();
    hash_setter.add(name.begin, name.end);
    hash_setter.add('=');
    switch(a->type()) {
    case string_id: {
	hash_setter.add('s');
	string_t v = a->string_value();
	hash_setter.add(v.begin, v.end);
	hash_setter.set_bits(b);
	break;
    }
    case int_id: {
	hash_setter.add('i');
	hash_setter.add(a->int_value());
	hash_setter.set_bits(b);
	break;
    }
    case double_id: {
	hash_setter.add('d');
	hash_setter.add_double(a->double_value());
	hash_setter.set_bits(b);
	break;
    }
    case bool_id: {
	if (a->bool_value()) {
	    hash_setter.add('T');
	    hash_setter.set_bits(b);
	} else {
	    hash_setter.add('F');
	    hash_setter.set_bits(b);
	}
	break;
    }
    case anytype_id:
	hash_setter.add('*');
	hash_setter.set_bits(b);
	break;
    }
}

//
// encode_equals encodes a constraint or an attribute as an
// *existence* category.
//
template<class bset_type>
void encode_exists(bset_type & b, const attribute * a) throw() {
    k_hash_setter<bset_type> hash_setter;

    siena::string_t name = a->name();
    hash_setter.add(name.begin, name.end);
    hash_setter.add('*');
    char c;
    switch(a->type()) {
    case string_id: 
	c = 's';
	break;
    case int_id: 
    case double_id: 
	c = 'n';
	break;
    case bool_id: 
	c = 'b';
	break;
    case anytype_id:
    default:
	c = '*';
	break;
    }
    hash_setter.add(c);
    hash_setter.set_bits(b);
}

template<class bset_type>
void encode_constraint(bset_type & b, const constraint * c) throw() {
    if (c->op() == eq_id && c->type() != anytype_id) {
	encode_equals(b, c); 
    } else {
	encode_exists(b, c); 
    } 
}

template<class bset_type>
void encode_attribute(bset_type & b, const attribute * a) throw() {
    if (a->type() != anytype_id)
	encode_equals(b, a); 
    encode_exists(b, a); 
}

template<class bset_type>
void encode_filter(bset_type & b, const filter * f) throw() {
    filter::iterator * fi = f->first();
    if (!fi) return;

    do {
	encode_constraint(b, fi); 
    } while (fi->next());
    delete(fi);
}

template<class bset_type>
void encode_message(bset_type & b, const message * m) throw() {
    message::iterator * mi  = m->first();
    if (!mi) return;

    do {
	encode_attribute(b, mi); 
    } while (mi->next());
    delete(mi);
}

} // end namespace siena

#endif
